#include <cstdio>
#include <pthread.h>
#include <thread>
#include <iostream>
#include <chrono>

template <typename... Args> void print(const Args &...args)
{
    ((std::cout << args << " "), ...);
    std::cout << std::endl;
}

void thread1_entry(const std::string &str1) { print(str1); }
void thread2_entry(const std::string &str1) { print(str1); }

class Test
{
public:
    void operator()(const std::string &str1 = "hello") const { print(str1); }
};

class Thread_guard
{
private:
    std::thread &_t;

public:
    explicit Thread_guard(std::thread &t) : _t(t) {}
    ~Thread_guard()
    {
        if (_t.joinable())
        {
            _t.join();
        }
    }
    Thread_guard(const Thread_guard &) = delete;
    Thread_guard &operator=(const Thread_guard &) = delete;
};

void func1()
{
    std::thread t1([](const std::string &str) -> void { print(str); },
                   "hello str");
    t1.join();
}
void func2()
{
    std::thread t1([]() {
        print("hello world");
        std::this_thread::sleep_for(std::chrono::seconds(1));
    });
    t1.detach();

    // 主线程不能先退出,否则detach线程可能会崩溃,如果正在进行数据写入,会出现问题
    // 线程崩溃后还是需要join的否则就变成僵尸进程了, 正常运行结束后会被自动join
    // 所以传参时尽量为拷贝, 不要引用,也不要传递局部变量
    std::this_thread::sleep_for(std::chrono::seconds(2));
}
struct func
{
    int &_i;
    func(int &i) : _i(i) {}
    void operator()()
    {
        for (int i = 0; i < 3; i++)
        {
            _i = i;
            std::cout << "_i is " << _i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
};
// void func_test(int &a) { print(a); }
void change_param(int &argu) { ++argu; }
void func3()
{
    int local_var = 0;
    func myfunc(local_var);
    std::thread func_thread{myfunc};
    Thread_guard g(func_thread);
    print("thread guard finish");
}

void func4(int n)
{
    print("call before n = ", n);
    // 语法检查通过, 但是编译报错
    // 需要使用std::ref()传递引用

    /**
     * 启动流程: 调用构造函数explicit thread(_Callable&& __f, _Args&&... __args)
     然后通过这里去转发
     decay会使参数从引用衰变为值拷贝
     template<typename... _Tp>
      using _Call_wrapper = _Invoker<tuple<typename decay<_Tp>::type...>>;

      这里的_State_impl需要两个模板参数,这里的_Wrapper会去调用_Invoker去拷贝参数,他有一个成员变量_M_t,
     把函数地址和参数都存放在这里 _Invoker overload了
     operator(),内部会把参数取出来,最后__invoke执行真正的函数
     _State_ptr(new _State_impl<_Wrapper>(), ...)
     _Invoker<tuple<typename decay<_Tp>::type...>>;
std::thread 需要把这个 _Invoker 传给操作系统创建的线程。但 _Invoker
是个模板类型，操作系统的接口（如 pthread_create）只认 void*。
通过继承多态实现了类型擦除
 struct _State
    {
      virtual ~_State();
      virtual void _M_run() = 0;
    };
    using _State_ptr = unique_ptr<_State>;

  private:
    template<typename _Callable>
      struct _State_impl : public _State
      {
        _Callable		_M_func;

        template<typename... _Args>
          _State_impl(_Args&&... __args)
          : _M_func(std::forward<_Args>(__args)...)
          { }

     经过完美转发将参数组织好后,产生一个void(*)()的函数指针,就是下面这个函数的最后一个参数
     然后通过在这个函数指针内部调用组织好的函数
     _M_start_thread(_State_ptr(new _State_impl<_Wrapper>(
              std::forward<_Callable>(__f), std::forward<_Args>(__args)...)),
            _M_thread_deps_never_run);
      }
     */
    std::thread t1(change_param, std::ref(n));
    t1.join();
    // join后t1线程做的修改才能被其他线程看到
    print("call after n = ", n);
}

int main()
{
    // get_id() maybe cause unexpected behavior in a multi-thread
    // std::thread t1(thread1_entry, "hello");
    // std::thread t1(Test(), "hello");
    // print("1. t1 thread id: ", t1.get_id());

    // std::thread t2(std::move(t1));
    // print("self()", pthread_self());
    // t1.join();
    // print("2. t1 thread id: ", t1.get_id());
    // t2.join();

    // t1被编译器解析为函数声明了,
    // Test()被解析为参数了,而不是被解析为匿名对象 std::thread t1(Test());
    // 编译器认为t1是一个函数, 报了这个错Member reference base type
    // 'std::thread (Test (*)())' is not a structure or union
    // 因为t1不是结构体也不是联合体, 所以他不能调用join函数
    // ,可以,在后面加上operator()(str)的参数, 这样就匹配别的构造函数了
    // 也可以使用{}初始化, 或者多加一个(),表示这是一个表达式 t1.join();
    // std::thread t1((Test()));
    // t1.join();
    // std::thread t2{Test()};
    // t2.join();
    // std::thread t3([]() -> void { print("hello world"); });
    // 线程不join的话会引发异常
    // t3.join();
    // std::thread t1(thread1_entry, "hello");
    // print(t1.get_id());
    // std::thread t2(std::move(t1));
    // print(t2.get_id());
    // 如果线程被move了是不需要join的, join会抛system error
    // t1.join();
    // t2.join();
    // func1();
    // func2();
    // func3();
    // int b = 10;
    // func_test(b);
    func4(10);
    return 0;
}
